using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Drawing;
using SchEdu.Framework.Logging;
using SchEdu.Framework.DataModel;
using SchEdu.Framework.Exceptions;
using SchEdu.Framework.Validators;

namespace SchEdu.Framework.Algorithm
{

    public abstract class Algorithm : IAlgorithm
    {

        #region Constructors
        public Algorithm()
        {
        }

        public Algorithm(SchEduData schEduData)
        {
            this.schEduData = schEduData;
        }
        #endregion

        #region Logger
        private Logger logger = new Logger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.ToString());
        protected Logger Logger
        {
            get { return logger; }
            set { logger = value; }
        }
        #endregion

        SchEduData schEduData = null;

        public SchEduData SchEduData
        {
            get { return schEduData; }
            set { schEduData = value; }
        }

        protected int time = 0;

        SchEduDataSet.SolutionRow solution = null;
        public SchEduDataSet.SolutionRow Solution
        {
            get { return solution; }
        }

        OrderList orders = new OrderList();

        public OrderList Orders
        {
            get { return orders; }
        }

        OperationList operations = new OperationList();

        public OperationList Operations
        {
            get { return operations; }
        }

        WorkCenterList workCenters = new WorkCenterList();

        public WorkCenterList WorkCenters
        {
            get { return workCenters; }
        }

        MachineList machines = new MachineList();

        public MachineList Machines
        {
            get { return machines; }
        }

        DependencyList dependencies = new DependencyList();

        public DependencyList Dependencies
        {
            get { return dependencies; }
        }

        SetupTimeList setupTimes = new SetupTimeList();

        public SetupTimeList SetupTimes
        {
            get { return setupTimes; }
        }

        AllocationList allocations = new AllocationList();

        public AllocationList Allocations
        {
            get { return allocations; }
        }

        WorkCenterOperationList workCenterOperationList = new WorkCenterOperationList();

        public WorkCenterOperationList WorkCenterOperationList
        {
            get { return workCenterOperationList; }
        }

        OrderOperationSortedList orderOperationSortedList = new OrderOperationSortedList();

        public OrderOperationSortedList OrderOperationSortedList
        {
            get { return orderOperationSortedList; }
        } 

        protected ValidatorList validators = new ValidatorList();

        protected abstract void Logic();

        #region running time
        DateTime startTotal;
        DateTime endTotal;
        DateTime startLogic;
        DateTime endLogic;
        #endregion

        #region IAlgorithm Members

        public virtual SchEduDataSet.SolutionRow Run()
        {

            time = 0;

            startTotal = DateTime.Now;

            Commit();

            Initialize();

            schEduData.RefreshAdditionalFields();

            Validate();

            CreateSolution();

            startLogic = DateTime.Now;
            Logic();
            endLogic = DateTime.Now;

            CreateAllocations();

            endTotal = DateTime.Now;

            solution.TotalTimeSecs = (decimal)(endTotal - startTotal).TotalSeconds;
            solution.LogicTimeSecs = (decimal)(endLogic - startLogic).TotalSeconds;

            Commit();

            return solution;
        }

        protected virtual void Commit()
        {
            schEduData.SchEduDataSet.AcceptChanges() ;
        }


        protected virtual void Initialize()
        {
            InitalizeGuid();

            InitializeColor();

            InitializeOrderList();

            InitializeOperationList();

            InitializeDependencyList();

            InitializeSetupTimeList();

            InitializeWorkCenterList();

            InitializeMachineList();

            InitializeWorkCenterOperationList();

            InitializeOrderOperationSortedList();

            InitializeAllocationList();

            InitializeValidators();

        }

        protected virtual void InitializeColor()
        {
            foreach (SchEduDataSet.OrderRow row in schEduData.Order)
            {
                if (row.IsColorNull())
                {
                    Color c = ColorGenerator.GenerateRandomColor();
                    row.Color = ColorTranslator.ToHtml(c);
                }
            }
            foreach (SchEduDataSet.MachineRow row in schEduData.Machine)
            {
                if (row.IsColorNull())
                {
                    Color c = ColorGenerator.GenerateRandomColor();
                    row.Color = ColorTranslator.ToHtml(c);
                }
            }
        }

        public static class ColorGenerator
        {
            static Random random = new Random();
            public static Color GenerateRandomColor()
            {
                int red = random.Next(256);
                int green = random.Next(256);
                int blue = random.Next(256);
                Color color = Color.FromArgb(red, green, blue);
                return color;
            }

        }
        protected virtual void InitalizeGuid()
        {
            foreach (SchEduDataSet.OrderRow row in schEduData.Order)
            {
                if (row.IsGuidNull())
                {
                    row.Guid = System.Guid.NewGuid();
                }
            }
            foreach (SchEduDataSet.OperationRow row in schEduData.Operation)
            {
                if (row.IsGuidNull())
                {
                    row.Guid = System.Guid.NewGuid();
                }
            }
            foreach (SchEduDataSet.WorkCenterRow row in schEduData.WorkCenter)
            {
                if (row.IsGuidNull())
                {
                    row.Guid = System.Guid.NewGuid();
                }
            }
            foreach (SchEduDataSet.MachineRow row in schEduData.Machine)
            {
                if (row.IsGuidNull())
                {
                    row.Guid = System.Guid.NewGuid();
                }
            }
        }

        protected virtual void InitializeOrderList()
        {
            orders = new OrderList(schEduData.Order);
        }

        protected virtual void InitializeOperationList()
        {
            operations = new OperationList(schEduData.Operation);
        }

        protected virtual void InitializeWorkCenterList()
        {
            workCenters = new WorkCenterList(schEduData.WorkCenter);
        }

        protected virtual void InitializeMachineList()
        {
            machines = new MachineList(schEduData.Machine);
        }

        protected virtual void InitializeDependencyList()
        {
            dependencies = new DependencyList(schEduData.Dependency);
        }

        protected virtual void InitializeSetupTimeList()
        {
            setupTimes = new SetupTimeList(schEduData.SetupTime);
        }

        protected virtual void InitializeAllocationList()
        {
            allocations = new AllocationList(schEduData.Allocation);
        }

        protected virtual void InitializeWorkCenterOperationList()
        {
            workCenterOperationList = new WorkCenterOperationList();
            foreach(SchEduDataSet.WorkCenterRow workCenter in schEduData.WorkCenter)
            {
                workCenterOperationList.Add(new WorkCenterKey(workCenter), new OperationList(workCenter.GetOperationRows()));
            }
        }


        protected virtual void InitializeOrderOperationSortedList()
        {
            orderOperationSortedList = new OrderOperationSortedList();
            foreach (SchEduDataSet.OrderRow order in schEduData.Order)
            {
                orderOperationSortedList.Add(order, new OperationSortedList(order.GetOperationRows()));
            }
        }

        protected virtual void InitializeValidators()
        {
            validators = new ValidatorList();
            validators.Add(new SchEduDataValidator(schEduData));
            validators.Add(new OrderValidator(schEduData));
            validators.Add(new MachinePerWorkCenterValidator(schEduData, 1, -1));
            validators.Add(new OperationPerOrderValidator(schEduData, 1, -1));
            validators.Add(new OperationProcessingTimeValidator(schEduData));
            validators.Add(new OperationWorkCenterValidator(schEduData));
        }


        protected virtual void Validate()
        {
            foreach (IValidator validator in validators)
            {
                validator.Validate();
            }
        }


        public abstract AboutInfo AboutInfo
        {
            get;
        }

        public string Company
        {
            get
            {
                return AboutInfo.Company;
            }
        }

        public string Copyright
        {
            get
            {
                return AboutInfo.Copyright;
            }
        }

        public string Description
        {
            get
            {
                return AboutInfo.Description;
            }
        }

        public string Product
        {
            get
            {
                return AboutInfo.Product;
            }
        }

        public string Title
        {
            get
            {
                return AboutInfo.Title;
            }
        }

        public string Version
        {
            get
            {
                return AboutInfo.Version;
            }
        }

        #endregion

        #region Helper Methods
        protected void CreateSolution()
        {
            solution = schEduData.Solution.NewSolutionRow();
            solution.CreatedOn = DateTime.Now;
            solution.Title = AboutInfo.Title;
            solution.Version = AboutInfo.Version;
            solution.Company = AboutInfo.Company;
            solution.Description = AboutInfo.Description;
            solution.Product = AboutInfo.Product;
            solution.Guid = AboutInfo.Guid;
            schEduData.Solution.AddSolutionRow(solution);

            Logger.Debug(string.Format("New solution created. ID:{0}", solution.ID));
        }

        public virtual void SetOperationDates()
        {
            foreach (SchEduDataSet.WorkCenterRow workCenter in SchEduData.WorkCenter)
            {
                workCenter.AvailabilityDate = 0;
            }
            foreach (SchEduDataSet.OrderRow order in SchEduData.Order)
            {
                order.AvailabilityDate = 0;
            }
            foreach (SchEduDataSet.MachineRow machine in SchEduData.Machine)
            {
                machine.AvailabilityDate = 0;
            }

            foreach (SchEduDataSet.OperationRow operation in SchEduData.Operation)
            {
                SchEduDataSet.WorkCenterRow workCenter = operation.WorkCenterRow;
                if (workCenter.GetMachineRows().Length == 0)
                {
                    operation.StartDate = Math.Max(workCenter.AvailabilityDate, operation.OrderRow.AvailabilityDate);
                    operation.EndDate = operation.StartDate + operation.ProcessingTime - 1;
                    workCenter.AvailabilityDate = operation.EndDate + 1;
                    operation.OrderRow.AvailabilityDate = operation.EndDate + 1;
                }
                else
                {
                    MachineList mc = new MachineList(workCenter.GetMachineRows());
                    SchEduDataSet.MachineRow machine = mc.GetFirstAvailable();
                    operation.MachineID = machine.ID;
                    operation.StartDate = Math.Max(machine.AvailabilityDate, operation.OrderRow.AvailabilityDate);
                    operation.EndDate = operation.StartDate + operation.ProcessingTime - 1;
                    machine.AvailabilityDate = operation.EndDate + 1;
                    operation.OrderRow.AvailabilityDate = operation.EndDate + 1;
                }
            }
        }

        public virtual void CreateAllocations()
        {
            foreach (SchEduDataSet.OperationRow operation in Operations)
            {
                SchEduDataSet.AllocationRow allocation = schEduData.Allocation.NewAllocationRow();
                allocation.SolutionID = Solution.ID;

                allocation.OrderID = operation.OrderID;
                allocation.OrderGuid = operation.OrderRow.Guid;
                allocation.Color = operation.OrderRow.Color;
                allocation.OrderNo = operation.OrderRow.OrderNo;

                allocation.OperationID = operation.ID;
                allocation.OperationGuid = operation.Guid;
                allocation.OperationNo = operation.OperationNo;

                allocation.WorkCenterID = operation.WorkCenterID;
                allocation.WorkCenterGuid = operation.WorkCenterRow.Guid;
                allocation.WorkCenter = operation.WorkCenterRow.Name;

                allocation.MachineID = operation.MachineID;
                allocation.MachineGuid = operation.MachineRow.Guid;
                allocation.Machine = operation.MachineRow.Name;

                allocation.StartDate = operation.StartDate;
                allocation.EndDate = operation.EndDate;
                allocation.ProcessingTime = operation.ProcessingTime;
                schEduData.Allocation.AddAllocationRow(allocation);
            }
        }


        #endregion

    }
}
